// 
// SoapTrace.Net - A soap message monitoring utility. 
// 
// Copyright (C) 2004-2007 Matthew Ward
// 
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
// 
// Matthew Ward (mrward@users.sourceforge.net)
// 

using SoapTrace.Core.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System;
using System.Text;
using WinPcap;

namespace SoapTrace.Core.Communication
{
	/// <summary>
	/// Represents a client's connection to the 
	/// <see cref="SoapMessageProxyServer"/>
	/// </summary>
	public class ProxyServerClientConnection : TcpSocketConnection
	{
		/// <summary>
		/// Soap response message received event.
		/// </summary>
		public event SoapResponseMessageReceivedEventHandler SoapResponseReceived;

		/// <summary>
		/// Soap request message received event.
		/// </summary>
		public event SoapRequestMessageReceivedEventHandler SoapRequestReceived;

		/// <summary>
		/// The ID of this connection.
		/// </summary>
		private ClientId m_Id = new ClientId( );

		/// <summary>
		/// The received soap request from the client.
		/// </summary>
		private SoapRequest m_Request = new SoapRequest( );

		/// <summary>
		/// The received soap response from the client.
		/// </summary>
		private SoapResponse m_Response = new SoapResponse( );
		
		/// <summary>
		/// The source of the request (IP + port).
		/// </summary>
		private string m_Source = String.Empty;

		/// <summary>
		/// Creates a new instance of the 
		/// <see cref="ProxyServerClientConnection"/> class.
		/// </summary>
		/// <param name="socket">The accepted client socket.</param>
		public ProxyServerClientConnection( Socket socket )
			: base( socket )
		{	
			try
			{
				IPEndPoint EndPoint = ( IPEndPoint )( socket.RemoteEndPoint );
				m_Source = EndPoint.ToString( );
			}
			catch( SocketException )
			{
				// Ignore.
			}
		}

		/// <summary>
		/// Bytes received from the client.
		/// </summary>
		/// <param name="bytes">A byte array.</param>
		/// <param name="length">The length of the byte array contain
		/// bytes.</param>
		protected override void BytesReceived( byte[ ] bytes, int length )
		{
			if( IsDebugLogLevel )
			{
				string BytesAsString = String.Empty;
				
				if( Logger.LogBytesAsString )
				{
					BytesAsString = ProxyServerClientConnection.GetBytesAsString( bytes, length );
				}
				else
				{
					BytesAsString = "\r\n" + Packet.GetRawHex( bytes, length );
				}

				Logger.LogDebug( String.Concat( "Client.BytesRecv: ", BytesAsString ) );
			}
			
			for( int i = 0; i < length; ++i )
			{
				if( m_Request.AddByte( bytes[ i ] ) )
				{
					m_Request.TimeReceived = DateTime.Now;
					m_Request.Source = m_Source;

					OnSoapRequestReceived( this, new SoapRequestReceivedEventArgs( m_Id, m_Request ) );

					m_Request = new SoapRequest( );
				}
			}
		}

		/// <summary>
		/// Bytes sent to the client.
		/// </summary>
		/// <param name="bytes">A byte array.</param>
		/// <param name="length">The length of the byte array contain
		/// bytes.</param>
		protected override void BytesSent( byte[ ] bytes, int length )
		{
			if( IsDebugLogLevel )
			{
				string BytesAsString = String.Empty;
				
				if( Logger.LogBytesAsString )
				{
					BytesAsString = ProxyServerClientConnection.GetBytesAsString( bytes, length );
				}
				else
				{
					BytesAsString = "\r\n" + Packet.GetRawHex( bytes, length );
				}

				Logger.LogDebug( String.Concat( "Client.BytesSent: ", BytesAsString ) );
			}
			
			for( int i = 0; i < length; ++i )
			{
				if( m_Response.AddByte( bytes[ i ] ) )
				{
					m_Response.TimeReceived = DateTime.Now;
					m_Response.Source = m_Source;

					OnSoapResponseReceived( this, new SoapResponseReceivedEventArgs( m_Id, m_Response ) );

					m_Response = new SoapResponse( );
				}
			}		
		}

		/// <summary>
		/// Soap request message received from a client connection.
		/// </summary>
		/// <param name="sender">The event source.</param>
		/// <param name="e">A <see cref="SoapRequestReceivedEventArgs"/> object.</param>
		private void OnSoapRequestReceived( object sender, SoapRequestReceivedEventArgs e )
		{
			if( SoapRequestReceived != null )
			{
				SoapRequestReceived( this, e );
			}
		}

		/// <summary>
		/// Soap response message received from a client connection.
		/// </summary>
		/// <param name="sender">The event source.</param>
		/// <param name="e">A <see cref="SoapResponseReceivedEventArgs"/> object.</param>
		private void OnSoapResponseReceived( object sender, SoapResponseReceivedEventArgs e )
		{
			if( SoapResponseReceived != null )
			{
				SoapResponseReceived( this, e );
			}
		}

		/// <summary>
		/// Gets a byte array as a formatted string.
		/// </summary>
		/// <param name="bytes">The byte array.</param>
		/// <param name="length">The length of the byte array.</param>
		/// <returns>A formatted string.</returns>
		private static string GetBytesAsString( byte[ ] bytes, int length )
		{
			string FormattedText = UnicodeEncoding.UTF8.GetString( bytes, 0, length );
			
			FormattedText = FormattedText.Replace( "\n", "\\n" );
			FormattedText = FormattedText.Replace( "\r", "\\r" );
			
			return FormattedText;
		}
		
		/// <summary>
		/// Gets whether the log level allows debug logging.
		/// </summary>
		private bool IsDebugLogLevel
		{
			get
			{
				bool IsDebugLogLevel = false;
				
				if( ( int )LogLevel.Debug >= ( int )Logger.Level )
				{
					IsDebugLogLevel = true;
				}
				
				return IsDebugLogLevel;
			}
		}
	}
}
